/*
 *  Classic "Hello, world" demonstration program
 *
 *  Copyright The Mbed TLS Contributors
 *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
 */

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

#include "mbedtls/build_info.h"

#include "mbedtls/platform.h"

#if defined(MBEDTLS_MD5_C)
#include "mbedtls/md5.h"
#endif

#if !defined(MBEDTLS_MD5_C)
int main(void)
{
    mbedtls_printf("MBEDTLS_MD5_C not defined.\n");
    mbedtls_exit(0);
}
#else

uint64_t get_file_size(FILE *stream);
void value2BigEndian(void *dest,const void *src,size_t count);
void value2LittleEndian(void *dest,const void *src,size_t count);

int main(int argc,char *argv[])
{
    unsigned char digest[16] = {0};
    unsigned char digest_len = sizeof(digest);
    if(argc != 3){
        printf("输入参数不正确！\r\n");
        printf("使用示例：");
        printf(".\\addMD5.exe input_file output_file\r\n");
        return -1; 
    }
    //打开原文件
    FILE *fp_src = fopen(argv[1],"rb");
    if(fp_src == NULL)
    {
        printf("open %s failed:%s\n",argv[1],strerror(errno));
        return -1;  
    }
    //创建目标文件
    FILE *fp_save = fopen(argv[2],"wb+"); 
    if(fp_save == NULL)
    {
        printf("create %s failed:%s\n",argv[2],strerror(errno));
        return -1;  
    }
    /*申请读缓冲*/
    uint8_t *read_buf = NULL;
    uint64_t read_len = 0;
    read_len = get_file_size(fp_src);
    read_buf = (uint8_t*)malloc(sizeof(char)*read_len + digest_len);
    if(read_buf == NULL)
    {
        fclose(fp_src);
        fclose(fp_save);
        remove(argv[2]);
        printf("内存申请失败\n");
        return -1;
    }

    size_t ret_code = fread(read_buf,sizeof(char),read_len,fp_src);
    if(ret_code != read_len)
    {
        if (feof(fp_src))
           printf("fopen(%s):%s\n", argv[1], strerror(errno));
        else if (ferror(fp_src)) {
           printf("fopen(%s):%s\n", argv[1], strerror(errno));
        }
        free(read_buf);
        fclose(fp_src);
        fclose(fp_save);
        remove(argv[2]);
        printf("读取文件失败\n");
        return -1;
    }

    int i, ret;


    if ((ret = mbedtls_md5((unsigned char *) read_buf, read_len, digest)) != 0) {
        free(read_buf);
        fclose(fp_src);
        fclose(fp_save);
        remove(argv[2]);
        mbedtls_exit(MBEDTLS_EXIT_FAILURE);
    }
    memcpy(read_buf + read_len, digest, digest_len);
    size_t r1  = fwrite(read_buf,sizeof(char),read_len + digest_len,fp_save);
    if(r1 != read_len + digest_len){
        if (feof(fp_save))
           printf("fwrite(%s):%s\n", argv[1], strerror(errno));
        else if (ferror(fp_save)) {
           printf("fwrite(%s):%s\n", argv[1], strerror(errno));
        }
        free(read_buf);
        fclose(fp_src);
        fclose(fp_save);
        remove(argv[2]);
        printf("写入文件失败\n");
        return -1; 
    }

    mbedtls_printf("\n  MD5('%s') = ", argv[1]);
    for (i = 0; i < digest_len; i++) {
        mbedtls_printf("%02x", digest[i]);
    }
    mbedtls_printf("\n\n");

    free(read_buf);
    fclose(fp_src);
    fclose(fp_save);
    mbedtls_exit(MBEDTLS_EXIT_SUCCESS);
}

/**
 * @brief   获取文件大小
 * 
 * @param   stream      
 * @return  uint64_t -1：获取失败 其他:获取成功
 */
uint64_t get_file_size(FILE *stream)
{
	uint64_t file_size = -1;
	uint64_t cur_offset = ftell(stream);	// 获取当前偏移位置
	if (cur_offset == (uint64_t)-1) {
		printf("ftell failed :%s\n", strerror(errno));
		return -1;
	}
    if (fseek(stream, 0, SEEK_END) != 0) {	// 移动文件指针到文件末尾
		printf("fseek failed: %s\n", strerror(errno));
		return -1;
	}
	file_size = ftell(stream);	// 获取此时偏移值，即文件大小
	if (file_size == (uint64_t)-1) {
        printf("ftell failed :%s\n", strerror(errno));
	}
    if (fseek(stream, cur_offset, SEEK_SET) != 0) {	// 将文件指针恢复初始位置
		printf("fseek failed: %s\n", strerror(errno));
		return -1;
	}
	return file_size;
}

/**
 * @brief   将src所指向的数据转换为大端序
 * 
 * @param   dest     目标地址   
 * @param   src      源地址   
 * @param   count    被转换数据的大小(单位字节)   
 */
void value2BigEndian(void *dest,const void *src,size_t count)
{
    /*判断自身的存储方式*/
    int a = 0x01;
    if(*(char *)&a == 0x01)/*小端*/
    {
        for(size_t i = 0;i < count;i++)
        {
            *((char *)dest+count-1-i) = *((char *)src+i);
        }
        return;
    }
    memcpy(dest,src,count);
}

/**
 * @brief   将src所指向的数据转换为小端序
 * 
 * @param   dest     目标地址   
 * @param   src      源地址   
 * @param   count    被转换数据的大小(单位字节)   
 */
void value2LittleEndian(void *dest,const void *src,size_t count)
{
    /*判断自身的存储方式*/
    int a = 0x01;
    if(*(char *)&a == 0x01)/*小端*/
    {
        memcpy(dest,src,count);
        return;
    }
        for(size_t i = 0;i < count;i++)
    {
        *((char *)dest+count-1-i) = *((char *)src+i);
    }
}

#endif /* MBEDTLS_MD5_C */
